const fs = require('fs-extra');
const path = require('path');

/**
 * 文件工具函数集
 * @class FileUtils
 */
class FileUtils {
  /**
   * 检查路径是否存在
   * @param {string} filePath - 文件或目录路径
   * @returns {Promise<boolean>} 是否存在
   */
  static async exists(filePath) {
    try {
      await fs.access(filePath);
      return true;
    } catch {
      return false;
    }
  }

  /**
   * 确保目录存在，不存在则创建
   * @param {string} dirPath - 目录路径
   * @returns {Promise<void>}
   */
  static async ensureDir(dirPath) {
    try {
      await fs.ensureDir(dirPath);
    } catch (error) {
      throw new Error(`创建目录失败: ${dirPath} - ${error.message}`);
    }
  }

  /**
   * 读取文件内容
   * @param {string} filePath - 文件路径
   * @param {Object} options - 读取选项
   * @returns {Promise<string>} 文件内容
   */
  static async readFile(filePath, options = {}) {
    try {
      return await fs.readFile(filePath, 'utf-8');
    } catch (error) {
      throw new Error(`读取文件失败: ${filePath} - ${error.message}`);
    }
  }

  /**
   * 写入文件内容
   * @param {string} filePath - 文件路径
   * @param {string} content - 文件内容
   * @param {Object} options - 写入选项
   * @returns {Promise<void>}
   */
  static async writeFile(filePath, content, options = {}) {
    try {
      // 确保目录存在
      const dirPath = path.dirname(filePath);
      await this.ensureDir(dirPath);

      // 写入文件
      await fs.writeFile(filePath, content, 'utf-8');
    } catch (error) {
      throw new Error(`写入文件失败: ${filePath} - ${error.message}`);
    }
  }

  /**
   * 获取相对路径
   * @param {string} fromPath - 起始路径
   * @param {string} toPath - 目标路径
   * @returns {string} 相对路径
   */
  static getRelativePath(fromPath, toPath = process.cwd()) {
    return path.relative(toPath, fromPath);
  }

  /**
   * 获取绝对路径
   * @param {string} filePath - 文件路径
   * @returns {string} 绝对路径
   */
  static getAbsolutePath(filePath) {
    return path.resolve(filePath);
  }

  /**
   * 检查是否为 .vue 文件
   * @param {string} filePath - 文件路径
   * @returns {boolean} 是否为 .vue 文件
   */
  static isVueFile(filePath) {
    return path.extname(filePath).toLowerCase() === '.vue';
  }

  /**
   * 获取文件扩展名
   * @param {string} filePath - 文件路径
   * @returns {string} 扩展名（含点）
   */
  static getExtension(filePath) {
    return path.extname(filePath);
  }

  /**
   * 获取文件名（不含扩展名）
   * @param {string} filePath - 文件路径
   * @returns {string} 文件名
   */
  static getBaseName(filePath) {
    return path.basename(filePath, path.extname(filePath));
  }

  /**
   * 获取目录路径
   * @param {string} filePath - 文件路径
   * @returns {string} 目录路径
   */
  static getDirName(filePath) {
    return path.dirname(filePath);
  }

  /**
   * 获取文件大小
   * @param {string} filePath - 文件路径
   * @returns {Promise<number>} 文件大小（字节）
   */
  static async getFileSize(filePath) {
    try {
      const stats = await fs.stat(filePath);
      return stats.size;
    } catch (error) {
      throw new Error(`获取文件大小失败: ${filePath} - ${error.message}`);
    }
  }

  /**
   * 格式化文件大小
   * @param {number} bytes - 字节数
   * @returns {string} 格式化后的大小
   */
  static formatFileSize(bytes) {
    if (bytes === 0) return '0 B';

    const k = 1024;
    const sizes = ['B', 'KB', 'MB', 'GB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));

    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
  }

  /**
   * 检查文件是否为二进制文件
   * @param {string} filePath - 文件路径
   * @returns {Promise<boolean>} 是否为二进制文件
   */
  static async isBinaryFile(filePath) {
    try {
      const buffer = await fs.readFile(filePath);

      // 检查文件开头的字节
      for (let i = 0; i < Math.min(buffer.length, 1000); i++) {
        const byte = buffer[i];

        // 检查是否为空字节（二进制文件特征）
        if (byte === 0) return true;

        // 检查是否为控制字符（排除常见的控制字符）
        if (byte < 32 && byte !== 9 && byte !== 10 && byte !== 13) return true;
      }

      return false;
    } catch {
      // 读取失败时假设为二进制文件
      return true;
    }
  }

  /**
   * 获取文件行数
   * @param {string} filePath - 文件路径
   * @returns {Promise<number>} 行数
   */
  static async getLineCount(filePath) {
    try {
      const content = await this.readFile(filePath);
      return content.split('\n').length;
    } catch (error) {
      throw new Error(`计算行数失败: ${filePath} - ${error.message}`);
    }
  }

  /**
   * 检查是否应该忽略的目录或文件
   * @param {string} name - 文件或目录名
   * @returns {boolean} 是否应该忽略
   */
  static shouldIgnore(name) {
    const ignoreList = [
      // 常见的目录和文件
      'node_modules', '.git', '.vscode', '.idea',
      'dist', 'build', 'coverage', '.nyc_output',
      // 常见的配置文件和缓存文件
      '.DS_Store', 'Thumbs.db', '*.log', '*.tmp',
      // 包管理器
      'package-lock.json', 'yarn.lock', 'pnpm-lock.yaml'
    ];

    // 支持通配符匹配
    return ignoreList.some(pattern => {
      if (pattern.includes('*')) {
        const regex = new RegExp(pattern.replace(/\*/g, '.*'));
        return regex.test(name);
      }
      return name === pattern;
    });
  }

  /**
   * 创建备份文件
   * @param {string} filePath - 原文件路径
   * @param {string} backupSuffix - 备份文件后缀
   * @returns {Promise<string>} 备份文件路径
   */
  static async createBackup(filePath, backupSuffix = '.backup') {
    try {
      const backupPath = filePath + backupSuffix;
      await fs.copy(filePath, backupPath);
      return backupPath;
    } catch (error) {
      throw new Error(`创建备份失败: ${filePath} - ${error.message}`);
    }
  }

  /**
   * 清理临时文件
   * @param {string} dirPath - 目录路径
   * @param {number} maxAge - 最大年龄（毫秒）
   * @returns {Promise<number>} 清理的文件数量
   */
  static async cleanupTempFiles(dirPath, maxAge = 24 * 60 * 60 * 1000) { // 默认24小时
    try {
      let cleanedCount = 0;
      const files = await fs.readdir(dirPath);
      const now = Date.now();

      for (const file of files) {
        const filePath = path.join(dirPath, file);
        const stats = await fs.stat(filePath);

        // 删除过期的临时文件
        if (now - stats.mtime.getTime() > maxAge &&
            (file.includes('.tmp') || file.includes('.temp'))) {
          await fs.remove(filePath);
          cleanedCount++;
        }
      }

      return cleanedCount;
    } catch (error) {
      // 如果目录不存在或无法读取，返回0
      return 0;
    }
  }
}

module.exports = FileUtils;